﻿Imports System.IO
Imports System.Web
Imports System.Net
Imports System.Text

Public Class Download
    Implements IHttpHandler

    Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest

        Dim strFilePath As String
        Dim strContentType As String
        strFilePath = Trim(context.Request.QueryString("filepath"))
        strContentType = Trim(context.Request.QueryString("contenttype"))

        'FilePath 为相对于 MAP 的路径，但这里的解释为相对于 Download 目录的路径，所以需要向上一级
        If strFilePath.StartsWith("/") Or strFilePath.StartsWith("\") Then
            strFilePath = context.Server.MapPath(strFilePath)
        Else
            strFilePath = context.Server.MapPath("../" & strFilePath)
        End If

        '文件不存在
        If Not File.Exists(strFilePath) Then
            context.Response.Status = "404 Not Found"
            context.Response.End()
        End If

        '取实际文档扩展名
        Dim strExtension As String
        strExtension = Path.GetExtension(strFilePath)
        If strExtension.StartsWith(".") Then
            strExtension = strExtension.Substring(1)
        End If

        'web.config 中定义的允许上传的文件类型
        Dim strUpfileType As String
        strUpfileType = System.Configuration.ConfigurationSettings.AppSettings("UpFileType")

        '如果文件扩展名不在允许上传名单中，则不允许下载
        If InStr("," & strUpfileType & ",", "," & strExtension & ",", CompareMethod.Text) = 0 Then
            context.Response.Status = "404 Not Found"
            context.Response.End()
        End If

        '输出文件
        If strContentType = "" Then
            context.Response.ContentType = "application/octet-stream"
        Else
            context.Response.ContentType = strContentType
        End If

        '网页文件类型时，设置text/html为响应头内容类型
        If strExtension.ToLower = "htm" OrElse strExtension.ToLower = "html" OrElse strExtension.ToLower = "mht" Then
            context.Response.ContentType = "text/html"
        End If

        '指定作为附件下载，不在 IE 中直接打开
        Dim strAttachmentFileType As String
        strAttachmentFileType = System.Configuration.ConfigurationSettings.AppSettings("AttachmentFileType")
        If InStr("," & strAttachmentFileType & ",", "," & strExtension & ",", CompareMethod.Text) > 0 Then
            context.Response.AddHeader("Content-Disposition", "attachment")
        End If

        '获取web.config中配置的分段下载下限
        Dim splitDownloadSizeConfig As String = System.Configuration.ConfigurationSettings.AppSettings("SplitDownloadSize")
        Dim splitDownloadSize As Long = IIf(String.IsNullOrEmpty(splitDownloadSizeConfig), 40960 * 1024, CInt(splitDownloadSizeConfig) * 1024)
        '获取文件实际大小
        Dim fileSize As Long = 0
        Dim response As FileWebResponse
        Try
            response = HttpWebRequest.Create(strFilePath).GetResponse()
            fileSize = response.ContentLength
        Catch ex As Exception
        Finally
            If response IsNot Nothing Then
                response.Close()
            End If
        End Try

        If fileSize < splitDownloadSize Then    '文件实际大小小于分段下载下限直接下载
            context.Response.WriteFile(strFilePath)
            context.Response.End()
        Else    '文件实际大小大于等于分段下载下限分段下载
            Dim stream As Stream = Nothing
            Dim buffer As Byte() = New [Byte](10000) {}
            Dim length As Integer
            Dim dataToRead As Long
            'Dim fileName As String = Path.GetFileName(strFilePath)
            Try
                stream = New FileStream(strFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)
                dataToRead = stream.Length
                While dataToRead > 0
                    If context.Response.IsClientConnected Then
                        '分段下载
                        length = stream.Read(buffer, 0, 10000)
                        context.Response.OutputStream.Write(buffer, 0, length)
                        context.Response.Flush()
                        buffer = New [Byte](10000) {}
                        dataToRead = dataToRead - length
                    Else
                        '客户端在没有下载完前，关闭连接，直接设置为-1,避免死循环
                        dataToRead = -1
                    End If
                End While
            Catch ex As Exception
                context.Response.Write("下载过程中发生异常:" & ex.Message)
            Finally
                If stream IsNot Nothing Then
                    stream.Close()
                End If
            End Try
        End If

    End Sub

End Class
